package com.simpou.commons.utils.lang;

import com.simpou.commons.utils.model.Pair;
import com.simpou.commons.utils.validation.Assertions;

import java.util.HashMap;
import java.util.Map;

/**
 * Use mapeador de enums nos casos em que os items de um enum são valores compostos que são associados a campos do
 * próprio enum. Operações para obter o item do enum a partir destes valores são beneficiadas com este mapeador. Ex.: um
 * enum tem dois elementos e cada elemento está associado a um inteiro que é campo do enum, é interessante em muitos
 * casos obter o item a partir deste inteiro, daí o mapeamento, para não ser necesssário percorrer todos os items do
 * enum em busca deste inteiro.
 *
 * @author Jonas Pereira
 * @since 2014-03-14
 */
public class EnumMapper<E extends Enum<E>, T> {

    private final Map<E, T> mappingDir = new HashMap<E, T>();

    private final Map<T, E> mappingRev = new HashMap<T, E>();

    /**
     * Registra um par formado pelo item do enum e seu identificador associado.
     *
     * @param entry Par enum e valor associado.
     * @throws java.lang.IllegalStateException Se chave ou valor do par já foram adicionados anteriormente.
     * @throws java.lang.NullPointerException Se par, chave ou valor for nulo.
     */
    public void put(final Pair<E, T> entry) {
        Assertions.notNull(entry);
        final E key = entry.getKey();
        final T value = entry.getValue();
        Assertions.notNull(key, value);
        if (mappingDir.containsKey(key)) {
            throw new IllegalStateException("Item already added: " + key);
        } else {
            mappingDir.put(key, value);
        }
        if (mappingRev.containsKey(value)) {
            throw new IllegalStateException("Item value already added: " + value);
        } else {
            mappingRev.put(value, key);
        }
    }

    /**
     * @param value Identificador do item do enum.
     * @return Item do enum associado ou null se não encontrar correspondências.
     */
    public E get(final T value) {
        return mappingRev.get(value);
    }

    /**
     * @param type Item do enum.
     * @return Valor associado ao item do enum ou null se não encontrar correspondências.
     */
    public T get(final E type) {
        return mappingDir.get(type);
    }
}
